package model

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	redisV8 "github.com/go-redis/redis/v8"
	"github.com/rabbitmq/amqp091-go"
	_logger "sprixin.com/rxdb_checker/logger"
)

type Configuration struct {
	Database Database `yaml:"db"`
	Redis    Redis    `yaml:"redis"`
	Rabbitmq Rabbitmq `yaml:"rabbitmq"`
	App      App      `yaml:"app"`
	IoTDB    IoTDB    `yaml:"iotdb"`
}

type Database struct {
	Host           *string `yaml:"host"`
	Port           *int    `yaml:"port"`
	User           *string `yaml:"user"`
	Password       *string `yaml:"password"`
	DbName         *string `yaml:"dbname"`
	MaxConnections *int    `yaml:"max_connections"`
}

type IoTDB struct {
	Host     *string `yaml:"host"`
	Port     *int    `yaml:"port"`
	User     *string `yaml:"user"`
	Password *string `yaml:"password"`
}

type Redis struct {
	Host           *string `yaml:"host"`
	Port           *int    `yaml:"port"`
	User           *string `yaml:"user"`
	Password       *string `yaml:"password"`
	ReadTimeout    *int    `yaml:"read_timeout"`
	WriteTimeout   *int    `yaml:"write_timeout"`
	Db             *int    `yaml:"db"`
	MaxConnections *int    `yaml:"max_connections"`
}

type Rabbitmq struct {
	Host           *string `yaml:"host"`
	Port           *int    `yaml:"port"`
	Username       *string `yaml:"user"`
	Password       *string `yaml:"password"`
	Vhost          *string `yaml:"vhost"`
	MaxConnections *int    `yaml:"max_connections"`
	conn           *amqp091.Connection
}

func (r *Rabbitmq) GetChannel() *amqp091.Channel {
	if r.conn == nil {
		r.conn = r.GetConnection()
		if r.conn == nil {
			panic("RabbitMQ连接失败")
		}
	}

	ch, err := r.conn.Channel()
	if err != nil {
		panic(err)
	}
	err = ch.Confirm(false) // 设置为false，避免在确认模式下发送消息
	if err != nil {
		panic(err)
	}

	return ch
}
func (r *Rabbitmq) GetConnection() *amqp091.Connection {
	conn, err := amqp091.DialConfig(fmt.Sprintf("amqp://%s:%s@%s:%d/%s", *r.Username, *r.Password, *r.Host, *r.Port, *r.Vhost), amqp091.Config{
		Properties: amqp091.Table{
			"connection_name": "rxdb_checker",
		},
	})
	if err != nil {
		panic(err)
	}
	errChan := make(chan *amqp091.Error)
	conn.NotifyClose(errChan)
	go func() {
		err := <-errChan
		_logger.GetLogger().Printf("RabbitMQ连接被关闭: %v", err)
	}()
	return conn
}

func (db Database) String() string {
	// 输出属性
	return fmt.Sprintf("host: %s, port: %d, user: %s, password: %s, dbname: %s, max_connections: %d", *db.Host, *db.Port, *db.User, *db.Password, *db.DbName, *db.MaxConnections)
}

func (redis Redis) String() string {
	// 输出属性
	return fmt.Sprintf("host: %s, port: %d, password: %s, db: %d, max_connections: %d", *redis.Host, *redis.Port, *redis.Password, *redis.Db, *redis.MaxConnections)
}
func (redis Redis) GetClient() *redisV8.Client {
	r := redisV8.NewClient(&redisV8.Options{
		Addr:         fmt.Sprintf("%s:%d", *redis.Host, *redis.Port),
		DB:           *redis.Db,
		MaxConnAge:   0,
		Password:     *redis.Password,
		ReadTimeout:  time.Second * time.Duration(*redis.ReadTimeout),
		WriteTimeout: time.Second * time.Duration(*redis.WriteTimeout),
	})
	return r
}

func (rabbitmq Rabbitmq) String() string {
	// 输出属性
	return fmt.Sprintf("host: %s, port: %d, username: %s, password: %s, vhost: %s, max_connections: %d", *rabbitmq.Host, *rabbitmq.Port, *rabbitmq.Username, *rabbitmq.Password, *rabbitmq.Vhost, *rabbitmq.MaxConnections)
}

func (c Configuration) String() string {
	// 输出属性
	return "db: " + c.Database.String() + ", redis: " + c.Redis.String() + ", rabbitmq: " + c.Rabbitmq.String()
}

const (
	VERSION_060       = "0.6.0"
	VERSION_100       = "1.0.0"
	VERSION_100_REDIS = "1.0.0-redis"
	VERSION_IOTDB     = "iotdb"
)

var (
	// 点位的value计数
	valueCounter atomic.Int64 = atomic.Int64{}
)

type App struct {
	CheckJarName *string  `yaml:"check_jar_name"`
	MessageDir   *string  `yaml:"message_dir"`
	AppVersion   *string  `yaml:"app_version"`
	HostGroupId  *string  `yaml:"hostgroup_id"`
	PackageSize  *int     `yaml:"package_size"`
	Interval     *int     `yaml:"interval"`
	Cyclecount   *int     `yaml:"cycle_count"`
	MutationRate *float32 `yaml:"mutation_rate"`
	WaitTime     *int     `yaml:"wait_time"`

	RedisCommand RedisCommandExecutor
	MqMessage    MqMessageGenerater
}

func (a *App) InitConfig() error {
	switch *a.AppVersion {
	case VERSION_060:
		fallthrough
	case VERSION_100:
		h := VersionStandHandler{}
		a.MqMessage = &h
		a.RedisCommand = &h
	case VERSION_100_REDIS:
		h := VersionRedisPrefHandler{}
		a.MqMessage = &h
		a.RedisCommand = &h
	case VERSION_IOTDB:
		h := VersionIoTDBHandler{}
		a.MqMessage = &h
	default:
		return fmt.Errorf("不支持的版本: %s", *a.AppVersion)
	}
	return nil
}

type RedisCommandExecutor interface {
	GetRedisValue(redisV8.Pipeliner, *Point, string) (*redisV8.StringCmd, string)
}

type MqMessageGenerater interface {
	GenerateMqMessage(int, string, []*Point) (*string, error)
}

type Checkable interface {
	Check(filteredCommlinkMapping []*Communicationlink, totalPointNum int)
}

type VersionStandHandler struct{}

func (h *VersionStandHandler) GetRedisValue(p redisV8.Pipeliner, point *Point, tableName string) (*redisV8.StringCmd, string) {
	key := tableName + "&" + strconv.Itoa(point.Id)
	return p.HGet(context.Background(), key, "timestamp"), key
}

func doBuildMqMessage(rtuid int, tableName string, points []*Point, getTime func() string) *string {
	logger := _logger.GetLogger()
	var builder strings.Builder
	builder.WriteString(`{"`)
	builder.WriteString(tableName)
	builder.WriteString(`":[`)
	timestamp := getTime() //time.Now().Format("2006-01-02 15:04:05.000")
	for _, p := range points {
		(*p.Counter)++
		// timestamp := time.Now().Format("2006-01-02 15:04:05.000")
		for timestamp <= p.Timestamp {
			logger.Printf("通道: %d, 点位id: %d, 时间戳回拨,新：%s, 旧：%s, 重新获取...\n", p.Communicationlink.Id, p.Id, timestamp, p.Timestamp)
			timestamp = getTime()
		}
		p.Timestamp = timestamp
		builder.WriteString(fmt.Sprintf(`[%d,%d,%d,"%s"],`, rtuid, p.PointNum, *p.Counter, timestamp))
	}

	// log.Println("写入消息数：", len(points))

	half := builder.String()
	result := half[:len(half)-1] + "]}"
	return &result
}

func doBuildMqMessageWithUniqValue(rtuid int, tableName string, points []*Point, getTime func() string) *string {
	logger := _logger.GetLogger()
	var builder strings.Builder
	builder.WriteString(`{"`)
	builder.WriteString(tableName)
	builder.WriteString(`":[`)
	timestamp := getTime() //time.Now().Format("2006-01-02 15:04:05.000")
	for _, p := range points {
		// timestamp := time.Now().Format("2006-01-02 15:04:05.000")
		for timestamp <= p.Timestamp {
			logger.Printf("通道: %d, 点位id: %d, 时间戳回拨,新：%s, 旧：%s, 重新获取...\n", p.Communicationlink.Id, p.Id, timestamp, p.Timestamp)
			timestamp = getTime()
		}
		p.Timestamp = timestamp
		builder.WriteString(fmt.Sprintf(`[%d,%d,%d,"%s"],`, rtuid, p.PointNum, valueCounter.Add(1), timestamp))
	}

	// log.Println("写入消息数：", len(points))

	half := builder.String()
	result := half[:len(half)-1] + "]}"
	return &result
}

func DoBuildMqMessageWithTimeStr(rtuid int, tableName string, points []*Point) *string {
	return doBuildMqMessageWithUniqValue(rtuid, tableName, points, func() string {
		return time.Now().Format("2006-01-02 15:04:05.000")
	})
}

func (h *VersionStandHandler) GenerateMqMessage(rtuid int, tableName string, points []*Point) (*string, error) {
	return DoBuildMqMessageWithTimeStr(rtuid, tableName, points), nil
}

type VersionRedisPrefHandler struct{}

func (h *VersionRedisPrefHandler) GetRedisValue(p redisV8.Pipeliner, point *Point, tableName string) (*redisV8.StringCmd, string) {
	key := tableName[:3] + "-t&" + strconv.Itoa(point.Id)
	return p.Get(context.Background(), key), key
}

func (h *VersionRedisPrefHandler) GenerateMqMessage(rtuid int, tableName string, points []*Point) (*string, error) {
	return DoBuildMqMessageWithTimeStr(rtuid, tableName, points), nil
}

type VersionIoTDBHandler struct{}

func (h *VersionIoTDBHandler) GenerateMqMessage(rtuid int, tableName string, points []*Point) (*string, error) {
	return doBuildMqMessageWithUniqValue(rtuid, tableName, points, func() string {
		return strconv.FormatInt(time.Now().UnixMilli(), 10)
	}), nil
}
